C++语言中有函数重载(overload)的功用,可以让相同功能的函数处理不同的数据类型,不过这样会造成程序代码重复的问题,这时就可以使用模板。所谓模板就是以相同程序代码处理不同数据类型的函数或类,在模板中以参数来代替数据类型,如此一来,就能在使用模板时,再设置数据类型来取代参数,产生一个新的函数或类。
模板根据不同的应用,分为函数模板(function template)和类模板(class tmeplate):
函数模板:在函数声明之前加上模板的声明,如此一来,就能以相同的函数,使用不同的数据类型。
template <class 模板参数行> 返回数据类型 函数名称(参数行) { 定义函数模板 return 返回值; }
template <class T> void swap(T* a, T* b) //可以是T { ... }
类模板:是先声明模板,将数据类型以模板参数取代,就可以在使用地才指定数据类型。
template <class 模板参数行> class 类名称 { 定义类模板 }
类模板的产生对象的方式:
类名称<数据类型> 对象名称; //声明一般对象
或
类名称<数据类型> 对象名称(); //此对象名称为对象和构造函数的合并声明
在C++之前的版本中,模板可以是函数模板或类模板。C++14还也可以创建变量模板。模板的一般规则,包括特化都适用于变量模板的声明和定义。
std::vector类型表示一个可变长度的数组,可以根据需要增长和收缩。C++11的std::array表示一个固定长度的数组,其长度在编译时确定。
C + +14将添加std::dynarray类型,它代表一个固定长度的数组,其大小在运行期构造对象时确定。std::dynarray类被明显地设计为当它被放置在栈上时(直接放置在栈上,或作为另一个栈对象的成员),可以使用栈内存而不是堆内存。
函数模板的原型为:
template <数据类型参数表> 返回值类型 函数名(参数表)
template <class T, class U> U func(T a, int b, U c);
类模板可以实例化成一个类,一个类才可以实例化成一个具体的对象。
C++是一种强类型语言,它要求程序中每一个对象的类型在编译阶段就能确定。一方面,这可以在很大程度上保证程序不会出现因类型问题而导致的错误。但另一方面,这种对类型的强力约束也限制了编码的灵活性,并且有可能导致编码效率的低下。泛型编程技术的引入使得两方面得到 了很好的平衡。
函数模板就是函数中的某个参数或返回值的类型是不确定的,是可变的,这些不确定的类型称为模板参数。如果给函数模板的模板参数指定了一个具体的类型,就得到了一个可以执行的函数,这个函数称为模板函数。函数模板可以节省程序员的工作量,若干个被处理的数据类型不同,但处理流程完全一样的函数可以写成一个函数模板。
什么是模板的实例化?
类模板只是个设计图纸,不是一个真正的类。要使得类模板变成一个真正的类,必须用真正的类型名或常量替换类模板的形式参数。这个过程称为类模板的实例化。实例化后,类模板成为了一个真正的类,可以定义这个类的对象了。
为什么要定义模板?定义类模板有什么好处?
有了类模板,可以将一组功能类似、存储方式也类似的类定义成一个类模板,可以进一步减少程序员的工作量。
同样是模板,为什么函数模板的使用与普通的函数完全一样,而类模板在使用时还必须被实例化?
在函数模板中,如果模板的形式参数出现在函数形式参数表中,那么当函数调用时,编译器可以根据函数的实际参数的类型确定模板的实际参数,然后对函数模板进行实例化。在定义类模板的对象时,无法确定模板形式参数对应的实际参数值,因此只能在程序中显式地指出模板实际参数的值。
函数模板在调用时中,会有赋值操作,而类模板对象时,没有涉及到这样的赋值操作,所以要显示指定类型,就像基本数据类型的定义或类实例化一样,前面要有类型信息。
函数模板的类型可以通过函数调用进行推断。
什么时候需要用到类模板的声明?为什么?
一般来说,当定义一个类模板是另一个类模板的友元时必须要用到类模板的声明。当类模板A声明类模板B是它的友元时,编译器必须知道有这样的一个类模板B存在。如果类模板B的定义出现在类模板A的定义前面,则没有问题。但如果类模板B定义在类模板A后面时,编译器就不知道B是什么,也无法确定类模板A中对类模板B的名是否合法,这时可以通过类模板的声明来告诉编译器B是一个类模板。
类模板继承时的语法与普通的类继承有什么不同?
类模板继承时,凡是涉及到基类的地方,都必须在基类名后面跟上模板的形式参数名。即:基类名<形式参数1,形式参数2,……>。
定义了一个类模板,在编译通过后为什么还不能确保类模板的语法是正确的?
由于类模板包含有模板参数,这些模板参数对应的实际参数在编译时尚未确定,所以编译器无法确定那些类型为模板参数的数据或函数的用法是否正确,只好暂时不检查。
函数模板显式实例化
template<class T> void output(T a) {cout<a<endl;} void main() { //隐式实例化 cout<"隐式实例化输出"<endl; output(1);//整型 output(1.2);//浮点型 cout<"显式实例化输出"<endl; //显示实例化 output(34);//整型 output (3.1415);//浮点型 }
函数模板是自动生成重载函数的方法;
C++中为什么用模板类。
(1) 可用来创建动态增长和减小的数据结构
(2) 它是类型无关的,因此具有很高的可复用性。
(3) 它在编译时而不是运行时检查数据类型,保证了类型安全。
(4) 它是平台无关的,有可移植性。
(5) 可用于基本数据类型。
类模板是一系列相关类的模板,类成员组成相同,成员函数的源代码形式相同,所不同的是所针对的类型。类模板的成员函数都是模板函数,在用类模板定义对象时,由于没有像函数实参表这样的额外信息渠道,因此无法按函数模板的方式省略模板实参。但可以为类模板的参数设置默认值。
编译器会对函数模板进行两次编译
在声明的地方对模板代码本身进行编译;在调用的地方对参数替换后的代码进行编译。
可以这样声明和使用类模板:
1) 先写出一个实际的类。由于其语义明确,含义清楚,一般不会出错。
2) 将此类中准备改变的类型名(如int要改变为float或char)改用一个自己指定的虚拟类型名(如上例中的numtype)。
3) 在类声明前面加入一行,格式为:
template <class 虚拟类型参数>
如:
template <class numtype> //注意本行末尾无分号 class Compare {…}; //类体